﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace KontoraME
{
    public partial class MapEditor : Form
    {
        //Đối tượng WorldMap lưu trữ tất cả thông tin của Map
        WorldMap worldmap;
        //PictureBox hình nền
        PictureBox pbBackground;
        //Vị trí giới hạn chiều rộng của map
        int axisX;
        //Vị trí giới hạn chiều cao của map
        int axisY;
        //Danh sách hình ảnh của các đối tượng game
        List<PictureBox> listObj;
        //Danh sách hình ảnh của các đối tượng game trong map
        List<PictureBox> listObjInMap;

        int listObjIndex = -1;
        //Hình ảnh của đối tượng game đang được chọn
        PictureBox selectedObj;
        //Rectangle làm đường biên để vẽ đối tượng Game
        Rectangle rect;
        //Đối tượng game hiện tại
        GameObject currentObj;
        //Vị trí cuộn trang trước đó
        Point lastScroll;
        //Vị trí chuột lúc nhấn chuột xuống
        Point startPoint;
        Point startPoint1;
        //Node gốc vẽ
        Node rootNode;
        //Node gốc va chạm
        Node rootNodeCollision;
        //Box hiện tại
        Body currentBody;
        //Rectangle box hiện tại
        Rectangle rectBox;
        //Biến đếm số lần Click
        int count = 0;
        //Biến lưu stage hiện tại
        Stage currentStage;
        //Biến lưu ID để đếm
        static int countID = 0;


        public MapEditor()
        {
            InitializeComponent();
            InitObjectsList();
            lastScroll = new Point(0, 0);
            currentObj = null;
            currentBody = null;
        }

        private void InitObjectsList()
        {
            listObj = new List<PictureBox>();
            listObjInMap = new List<PictureBox>();
            listObj.Add(pbRunningMan);
            listObj.Add(pbStandingMan);
            listObj.Add(pbBlockhouse);
            listObj.Add(pbMachineGun);
            listObj.Add(pbItemBlock);
            listObj.Add(pbItemFlying);
            listObj.Add(pbGrass);
            listObj.Add(pbWater);
            listObj.Add(pbBridge);
            listObj.Add(pbBoss1);
            listObj.Add(pbGrass1);
            listObj.Add(pbEnemyShelter);
            listObj.Add(pbSbox);
            listObj.Add(pbStoneFalling);
            listObj.Add(pbStoneFlying);
            listObj.Add(pbFire);
            listObj.Add(pbEnemyDiver);
            listObj.Add(pbBoss3);
            listObj.Add(pbEnemyMachine);
            listObj.Add(pbGrenade);
            listObj.Add(pbTank);
            listObj.Add(pbBoss5);

            PictureBox pb = new PictureBox();
            listObjInMap.Add(pbRunningMan);
            listObjInMap.Add(pbStandingMan);
            listObjInMap.Add(pbBlockhouse);
            listObjInMap.Add(pbMachineGun);
            listObjInMap.Add(pbItemBlock);
            pb.Name = "pbItemFlyingMap";
            pb.Image = Properties.Resources.item_bullet_flying_map;
            listObjInMap.Add(pb);
            listObjInMap.Add(pbGrass);
            listObjInMap.Add(pbWater);
            pb = new PictureBox();
            pb.Name = "pbBridgeMap";
            pb.Image = Properties.Resources.bridge_map;
            listObjInMap.Add(pb);
            pb = new PictureBox();
            pb.Name = "pbBoss1Map";
            pb.Image = Properties.Resources.boss1_map;
            listObjInMap.Add(pb);
            listObjInMap.Add(pbGrass1);
            pb = new PictureBox();
            pb.Name = "pbEnemyShelterMap";
            pb.Image = Properties.Resources.enemy_shelter_map;
            listObjInMap.Add(pb);
            listObjInMap.Add(pbSbox);
            listObjInMap.Add(pbStoneFalling);
            listObjInMap.Add(pbStoneFlying);
            pb = new PictureBox();
            pb.Name = "pbFireMap";
            pb.Image = Properties.Resources.fire_map;
            listObjInMap.Add(pb);
            pb = new PictureBox();
            pb.Name = "pbEnemyDiverMap";
            pb.Image = Properties.Resources.enemy_diver_map;
            listObjInMap.Add(pb);
            pb = new PictureBox();
            pb.Name = "pbBoss3Map";
            pb.Image = Properties.Resources.boss3_map;
            listObjInMap.Add(pb);
            listObjInMap.Add(pbEnemyMachine);
            pb = new PictureBox();
            pb.Name = "pbGrenadeMap";
            pb.Image = Properties.Resources.grenade_map;
            listObjInMap.Add(pb);
            pb = new PictureBox();
            pb.Name = "pbTankMap";
            pb.Image = Properties.Resources.tank_map;
            listObjInMap.Add(pb);
            pb = new PictureBox();
            pb.Name = "pbBoss5Map";
            pb.Image = Properties.Resources.boss5_map;
            listObjInMap.Add(pb);

            foreach (PictureBox p in listObj)
                p.Click += Obj_Click;
            selectedObj = listObjInMap[0];
            listObjIndex = 0;
            change_Obj(listObj[1].Name);
        }

        private void Obj_Click(object sender, EventArgs e)
        {
            change_Obj((sender as PictureBox).Name);
            cbbVar1.Items.Clear();
            cbbVar2.Items.Clear();
            switch ((sender as PictureBox).Name) 
            {
                case "pbFire":
                    cbbVar1.Items.Add("Left");
                    cbbVar1.Items.Add("Right");
                    cbbVar1.SelectedIndex = 0;
                    break;
                case "pbRunningMan":
                    cbbVar1.Items.Add("Left");
                    cbbVar1.Items.Add("Right");
                    cbbVar1.SelectedIndex = 0;
                    cbbVar2.Items.Add("OnlyOne");
                    cbbVar2.Items.Add("AutoBorn");
                    cbbVar2.SelectedIndex = 0;
                    break;
                case "pbBlockhouse":
                    cbbVar1.Items.Add("1");
                    cbbVar1.Items.Add("3");
                    cbbVar1.SelectedIndex = 0;
                    cbbVar2.Items.Add("Left");
                    cbbVar2.Items.Add("Right");
                    cbbVar2.SelectedIndex = 0;
                    break;
                case "pbStandingMan":
                case "pbEnemyShelter":
                    cbbVar1.Items.Add("1");
                    cbbVar1.Items.Add("3");
                    cbbVar1.SelectedIndex = 0;
                    break;
                case "pbBridge":
                case "pbBoss1":
                case "pbMachineGun":
                case "pbGrass":
                case "pbGrass1":
                case "pbWater":
                case "pbStoneFalling":
                case "pbStoneFlying":
                case "pbEnemyDiver":
                case "pbBoss3":
                case "pbEnemyMachine":
                case "pbGrenade":
                case "pbTank":
                case "pbBoss5":
                    break;
                case "pbItemBlock":
                case "pbItemFlying":
                    cbbVar1.Items.Add("M");
                    cbbVar1.Items.Add("B");
                    cbbVar1.Items.Add("F");
                    cbbVar1.Items.Add("S");
                    cbbVar1.Items.Add("L");
                    cbbVar1.Items.Add("R");
                    cbbVar1.Items.Add("Eagle");
                    cbbVar1.SelectedIndex = 0;
                    break;
            }
        }

        private void change_Obj(string name)
        {
            PictureBox new_Obj = (from pt in listObj
                                  where pt.Name == name
                                  select pt).First();
            selectedObj = listObj[listObjIndex];
            //Thay đổi màu nền của đối tượng được chọn trong danh sách menu
            if (new_Obj != null && selectedObj != new_Obj)
            {
                selectedObj.BackColor = Color.Transparent;
                selectedObj.BorderStyle = BorderStyle.None;
                selectedObj = new_Obj;
                listObjIndex = listObj.IndexOf(new_Obj);
                selectedObj.BorderStyle = BorderStyle.Fixed3D;
            }
            switch (name)
            {
                case "pbRunningMan":
                    lblVar1.Visible = true;
                    lblVar2.Visible = true;
                    cbbVar1.Visible = true;
                    cbbVar2.Visible = true;
                    lblVar1.Text = "Direction";
                    lblVar2.Text = "Born mode";
                    break;
                case "pbBridge":
                case "pbBoss1":
                case "pbWater":
                case "pbGrass":
                case "pbGrass1":
                case "pbMachineGun":
                case "pbSbox":
                case "pbEnemyMachine":
                case "pbGrenade":
                case "pbTank":
                case "pbBoss5":
                    lblVar1.Visible = false;
                    lblVar2.Visible = false;
                    cbbVar1.Visible = false;
                    cbbVar2.Visible = false;
                    break;
                case "pbBlockhouse":
                    lblVar1.Visible = true;
                    lblVar2.Visible = true;
                    cbbVar1.Visible = true;
                    cbbVar2.Visible = true;
                    lblVar1.Text = "Number of bullet";
                    lblVar2.Text = "Direction";
                    break;
                case "pbItemBlock":
                case "pbItemFlying":
                    lblVar1.Visible = true;
                    lblVar2.Visible = false;
                    cbbVar1.Visible = true;
                    cbbVar2.Visible = false;
                    lblVar1.Text = "Bullet type";
                    break;
                case "pbStandingMan":
                case "pbEnemyShelter":
                    lblVar1.Visible = true;
                    lblVar2.Visible = false;
                    cbbVar1.Visible = true;
                    cbbVar2.Visible = false;
                    lblVar1.Text = "Number of bullet";
                    break;
            }
        }

        //Xử lý button New
        private void btnNew_Click(object sender, EventArgs e)
        {
            worldmap = new WorldMap();
            pnlMapViewer.Controls.Clear();
            btnAddStage_Click(sender, e);
        }

        //Xử lý button AddStage
        private void btnAddStage_Click(object sender, EventArgs e)
        {
            if (worldmap != null)
            {
                AddStage asForm = new AddStage(worldmap);
                asForm.ShowDialog();
                DrawWorld();

                this.cbbStage.DisplayMember = "Name";
                this.cbbStage.ValueMember = "Value";
                this.cbbStage.Items.Clear();
                foreach (Stage st in worldmap.ListStages)
                    this.cbbStage.Items.Add(new { Name = "State " + worldmap.ListStages.IndexOf(st), Value = st });
                this.cbbStage.SelectedIndex = this.cbbStage.Items.Count - 1;
                if(this.cbbStage.SelectedIndex >= 0)
                    currentStage = worldmap.ListStages[this.cbbStage.SelectedIndex];
            }
        }

        private void DrawWorld()
        {
            if (worldmap.ListStages.Count == 0) return;
            pbBackground = new PictureBox();
            pbBackground.Location = new Point(0, 0);

            //Các biến lưu vị trí của map stage
            int strX = worldmap.ListStages[0].position.X;
            int strY = worldmap.ListStages[0].position.Y;
            int endX = 0;
            int endY = 0;

            //Lưu các biến vị trí của map stage để lưu giá trị lớn nhất 
            foreach (Stage st in worldmap.ListStages)
            {
                if (strX > st.position.X)
                    strX = st.position.X;
                if (endX < st.cols * GlobalConstants.TILE_SIZE + st.position.X)
                    endX = st.position.X + st.cols * GlobalConstants.TILE_SIZE;

                if (strY > st.position.Y)
                    strY = st.position.Y;
                if (endY < st.rows * GlobalConstants.TILE_SIZE + st.position.Y)
                    endY = st.position.Y + st.rows * GlobalConstants.TILE_SIZE;
            }

            this.axisX = strX;
            if (strX > 0)
                strX = 0;
            axisY = endY;

            int w = Math.Abs(endX - strX);
            int h = Math.Abs(endY - strY);

            pbBackground.Size = new Size(w, h);
            pnlMapViewer.Controls.Add(pbBackground);
            pbBackground.Click += pbBackground_Click;
            pbBackground.Paint += pbBackground_Paint;
            pbBackground.MouseDown += pbBackground_MouseDown;
            pbBackground.MouseMove += pbBackground_MouseMove;
            pbBackground.MouseUp += pbBackground_MouseUp;

            pnlMapViewer.AutoScrollPosition = lastScroll;

        }

        //Hàm xử lý sự kiện nhả chuột
        void pbBackground_MouseUp(object sender, MouseEventArgs e)
        {
            pbBackground.Invalidate();
        }

        //Hàm xác định vị trí chuột khi nhấn
        private void pbBackground_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.None)
                startPoint = e.Location;
        }

        //Hàm xử lý sự kiện di chuyển chuột
        void pbBackground_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.None)
            {
                Point changePoint = new Point(e.Location.X - startPoint.X, e.Location.Y - startPoint.Y);
                pnlMapViewer.AutoScrollPosition = new Point(
                    -pnlMapViewer.AutoScrollPosition.X - changePoint.X,
                    -pnlMapViewer.AutoScrollPosition.Y - changePoint.Y);

                lastScroll.X = -pnlMapViewer.AutoScrollPosition.X - changePoint.X;
                lastScroll.Y = -pnlMapViewer.AutoScrollPosition.Y - changePoint.Y;
                pbBackground.Invalidate();
            }

            //In ra tọa độ hiện tại của chuột
            int y = axisY - e.Location.Y;
            lblCurrentPos.Text = "X: " + e.Location.X + "\nY: " + y;
            #region Xử lý phím Ctrl
            if (Control.ModifierKeys == Keys.Control)
            {
                selectedObj = listObjInMap[listObjIndex];
                if (selectedObj != null)
                {
                    Point ip = new Point(e.Location.X, e.Location.Y);
                    if (chkAutoAlign.Checked)
                    {
                        ip.X = ip.X / GlobalConstants.STILE_SIZE * GlobalConstants.STILE_SIZE;
                        ip.Y = (ip.Y / GlobalConstants.STILE_SIZE + 1) * GlobalConstants.STILE_SIZE;
                        if (selectedObj.Image.Height < GlobalConstants.STILE_SIZE)
                        {
                            ip.Y -= GlobalConstants.STILE_SIZE - selectedObj.Image.Height;
                        }
                    }
                    else
                    {
                        ip.X -= GlobalConstants.STILE_SIZE;
                        ip.Y += GlobalConstants.STILE_SIZE;
                    }

                    ip.Y -= selectedObj.Image.Height;

                    Size sz = selectedObj.Image.Size;
                    rect = new Rectangle(ip, sz);
                    pbBackground.Invalidate();
                }
            }
            #endregion
        }

        //Hàm xử lý sự kiện click
        void pbBackground_Click(object sender, EventArgs e)
        {
            try
            {
                MouseEventArgs eM = e as MouseEventArgs;
                if (eM.Button == MouseButtons.Left)
                {
                    Point ip = new Point(eM.X, eM.Y);
                    #region Xử lý phím Ctrl
                    if (Control.ModifierKeys == Keys.Control)
                    {
                        selectedObj = listObjInMap[listObjIndex];
                        if (selectedObj != null)
                        {
                            if (chkAutoAlign.Checked)
                            {
                                //Xác định vị trí của tile mà đối tượng sẽ nằm trong đó
                                ip.X = ip.X / GlobalConstants.STILE_SIZE * GlobalConstants.STILE_SIZE;
                                ip.Y = (ip.Y / GlobalConstants.STILE_SIZE + 1) * GlobalConstants.STILE_SIZE;
                                if (selectedObj.Image.Height < GlobalConstants.STILE_SIZE)
                                {
                                    ip.Y -= GlobalConstants.STILE_SIZE - selectedObj.Image.Height;
                                }
                            }
                            else
                            {
                                ip.X -= GlobalConstants.STILE_SIZE;
                                ip.Y += GlobalConstants.STILE_SIZE;
                            }

                            ip.Y = axisY - ip.Y;
                            //Click chọn đối tượng game
                            GameObject tmpObj = GetObjectsAtPos(ip.X, ip.Y);
                            if (tmpObj != null)
                            {
                                cbbVar1.Text = tmpObj.Value1.ToString();
                                cbbVar2.Text = tmpObj.Value2.ToString();
                                if (tmpObj.getTypeNameById() == selectedObj.Name && tmpObj.Point == ip)
                                    return;
                            }
                            worldmap.tileNum = worldmap.ListStages[0].rows * worldmap.ListStages[0].cols;
                            countID++;
                            add(ip, countID + worldmap.tileNum);
                            Bitmap curs = new Bitmap(selectedObj.Image);
                            pnlMapViewer.Cursor = CustomCursor.CreateCursor(curs, selectedObj.Image.Width, selectedObj.Image.Height);
                        }
                    }
                    #endregion
                    #region Xử lý phím Shift
                    else if (Control.ModifierKeys == Keys.Shift)
                    {
                        GameObject tmpObj = GetObjectsAtPos(eM.X, axisY - eM.Y);
                        Body tmpBody = GetBodyAtPos(eM.X, axisY - eM.Y);
                        if (tmpObj == null && tmpBody == null)
                            return;
                        worldmap.ListObjects.Remove(tmpObj);
                        worldmap.ListBodies.Remove(tmpBody);                        
                    }
                    #endregion
                    #region Xử lý phím Alt
                    else if (Control.ModifierKeys == Keys.Alt)
                    {
                        if (!chkAutoAlign.Checked)
                        {
                            MessageBox.Show("Can not determine tiles of this box!");
                        }
                        else
                        {
                            if (IsBoxObject(selectedObj.Name))
                            {
                                if (count == 0)
                                {
                                    ip.X = ip.X / GlobalConstants.STILE_SIZE * GlobalConstants.STILE_SIZE;
                                    ip.Y = ip.Y / GlobalConstants.STILE_SIZE * GlobalConstants.STILE_SIZE;
                                    rectBox.X = ip.X;
                                    rectBox.Y = ip.Y;
                                    startPoint1.X = ip.X;
                                    startPoint1.Y = ip.Y;
                                    rectBox.Width = rectBox.Height = GlobalConstants.STILE_SIZE;
                                    count = 1;
                                }
                                else
                                {
                                    rectBox.X = (startPoint1.X > ip.X ? ip.X : startPoint1.X)
                                        / GlobalConstants.STILE_SIZE * GlobalConstants.STILE_SIZE;
                                    rectBox.Y = (startPoint1.Y > ip.Y ? ip.Y : startPoint1.Y)
                                        / GlobalConstants.STILE_SIZE * GlobalConstants.STILE_SIZE;
                                    rectBox.Width = startPoint1.X < ip.X ? (Math.Abs(startPoint1.X - ip.X)
                                        / GlobalConstants.STILE_SIZE) * GlobalConstants.STILE_SIZE + GlobalConstants.STILE_SIZE :
                                        (Math.Abs(startPoint1.X - ip.X)
                                        / GlobalConstants.STILE_SIZE + 1) * GlobalConstants.STILE_SIZE + GlobalConstants.STILE_SIZE;
                                    rectBox.Height = startPoint1.Y < ip.Y ? (Math.Abs(startPoint1.Y - ip.Y)
                                        / GlobalConstants.STILE_SIZE) * GlobalConstants.STILE_SIZE + GlobalConstants.STILE_SIZE :
                                        (Math.Abs(startPoint1.Y - ip.Y)
                                        / GlobalConstants.STILE_SIZE + 1) * GlobalConstants.STILE_SIZE + GlobalConstants.STILE_SIZE;
                                    countID++;
                                    addBox(rectBox, countID);
                                    count = 0;
                                }
                            }
                        }
                    }
                    #endregion
                    else
                    {
                        GameObject tmpObj = GetObjectsAtPos(eM.X, axisY - eM.Y);
                        Body tmpBody = GetBodyAtPos(eM.X, axisY - eM.Y);
                        if (tmpObj == null && tmpBody == null)
                            return;
                        if(tmpObj != null)
                            if (currentObj != tmpObj)
                            {
                                currentObj = tmpObj;
                                cbbVar1.Items.Clear();
                                cbbVar2.Items.Clear();
                                switch (currentObj.getTypeNameById()) 
                                {
                                    case "fire":
                                        lblVar1.Visible = true;
                                        lblVar2.Visible = false;
                                        cbbVar1.Visible = true;
                                        cbbVar2.Visible = false;
                                        cbbVar1.Items.Add("Left");
                                        cbbVar1.Items.Add("Right");
                                        cbbVar1.SelectedIndex = currentObj.Value1;
                                        break;
                                    case "running_man":
                                        lblVar1.Visible = true;
                                        lblVar2.Visible = true;
                                        cbbVar1.Visible = true;
                                        cbbVar2.Visible = true;
                                        lblVar1.Text = "Direction";
                                        lblVar2.Text = "Born mode";
                                        cbbVar1.Items.Add("Left");
                                        cbbVar1.Items.Add("Right");
                                        cbbVar1.SelectedIndex = currentObj.Value1;
                                        cbbVar2.Items.Add("OnlyOne");
                                        cbbVar2.Items.Add("AutoBorn");
                                        cbbVar2.SelectedIndex = currentObj.Value2;
                                        break;
                                    case "blockhouse":
                                        lblVar1.Visible = true;
                                        lblVar2.Visible = true;
                                        cbbVar1.Visible = true;
                                        cbbVar2.Visible = true;
                                        lblVar1.Text = "Number of bullet";
                                        lblVar2.Text = "Direction";
                                        cbbVar1.Items.Add("1");
                                        cbbVar1.Items.Add("3");
                                        cbbVar2.Items.Add("Left");
                                        cbbVar2.Items.Add("Right");
                                        cbbVar1.SelectedIndex = currentObj.Value1;
                                        cbbVar2.SelectedIndex = currentObj.Value2;
                                        break;
                                    case "standing_man":
                                    case "enemy_shelter":
                                        lblVar1.Visible = true;
                                        lblVar2.Visible = false;
                                        cbbVar1.Visible = true;
                                        cbbVar2.Visible = false;
                                        lblVar1.Text = "Number of bullet";
                                        cbbVar1.Items.Add("1");
                                        cbbVar1.Items.Add("3");
                                        cbbVar1.SelectedIndex = currentObj.Value1;
                                        break;
                                    case "bridge":
                                    case "boss1":
                                    case "machine_gun":
                                    case "grass":
                                    case "grass1":
                                    case "water":
                                    case "sbox":
                                    case "stone_falling":
                                    case "stone_flying":
                                    case "enemy_diver":
                                    case "boss3":
                                    case "enemy_machine":
                                    case "grenade":
                                    case "tank":
                                    case "boss5":
                                        lblVar1.Visible = false;
                                        lblVar2.Visible = false;
                                        cbbVar1.Visible = false;
                                        cbbVar2.Visible = false;
                                        break;
                                    case "item_block":
                                    case "item_flying":
                                        lblVar1.Visible = true;
                                        lblVar2.Visible = false;
                                        cbbVar1.Visible = true;
                                        cbbVar2.Visible = false;
                                        lblVar1.Text = "Bullet type";
                                        cbbVar1.Items.Add("M");
                                        cbbVar1.Items.Add("B");
                                        cbbVar1.Items.Add("F");
                                        cbbVar1.Items.Add("S");
                                        cbbVar1.Items.Add("L");
                                        cbbVar1.Items.Add("R");
                                        cbbVar1.Items.Add("Eagle");
                                        cbbVar1.SelectedIndex = currentObj.Value1;
                                        break;
                                }
                                rect.X = currentObj.Point.X;
                                rect.Y = axisY - currentObj.Point.Y - currentObj.Size.Height;
                                rect.Width = currentObj.Size.Width;
                                rect.Height = currentObj.Size.Height;
                                pbBackground.Invalidate();
                            }
                        if (tmpBody != null)
                            if (currentBody != tmpBody)
                            {
                                currentBody = tmpBody;
                                rectBox.X = currentBody.Point.X;
                                rectBox.Y = currentBody.Point.Y;
                                rectBox.Width = currentBody.Size.Width;
                                rectBox.Height = currentBody.Size.Height;
                                pbBackground.Invalidate();
                            }
                    }
                }
            }
            catch (Exception ex) 
            {
                Console.WriteLine(ex.StackTrace);
            }
        }
        //Kiểm tra tên của picturebox có phải là đối tượng box không
        private bool IsBoxObject(string p)
        {
            bool result = false;
            switch (p) 
            {
                case "pbGrass":
                case "pbWater":
                case "pbGrass1":
                case "pbSbox":
                result = true;
                break;
                default:
                result = false;
                break;
            }
            return result;

        }
        //Lấy vị trí box tại vị trí click
        private Body GetBodyAtPos(int x, int y)
        {
            if (worldmap.ListBodies.Count == 0)
                return null;
            foreach (Body b in worldmap.ListBodies)
            {
                Rectangle r = new Rectangle(new Point(b.Point.X, b.Point.Y), new Size(b.Size.Width, b.Size.Height));
                if (r.Contains(new Point(x, axisY - y)))
                {
                    return b;
                }
            }
            return null;
        }

        //Hàm thêm một đối tượng Box vào ListBox
        private void addBox(Rectangle rectBox, int id)
        {
            int type = 0;
            switch (selectedObj.Name) { 
                case "pbGrass":
                    type = GlobalConstants.GRASS_BODY_ID;
                    break;
                case "pbWater":
                    type = GlobalConstants.WATER_BODY_ID;
                    break;
                case "pbGrass1":
                    type = GlobalConstants.GRASS1_BODY_ID;
                    break;
                case "pbSbox":
                    type = GlobalConstants.SBOX_BODY_ID;
                    break;

            }
            Body b = new Body(id, new Point(rectBox.X, rectBox.Y), new Size(rectBox.Width, rectBox.Height), type);
            this.worldmap.ListBodies.Add(b);
            currentBody = b;
        }

        //Hàm thêm đối tượng game vào list game object
        private void add(Point ip, int id)
        {
            string name;
            switch (selectedObj.Name) 
            {
                case "pbRunningMan":
                    name = "running_man";
                    break;
                case "pbStandingMan":
                    name = "standing_man";
                    break;
                case "pbBlockhouse":
                    name = "blockhouse";
                    break;
                case "pbMachineGun":
                    name = "machine_gun";
                    break;
                case "pbItemBlock":
                    name = "item_block";
                    break;
                case "pbItemFlyingMap":
                    name = "item_flying";
                    break;
                case "pbBridgeMap":
                    name = "bridge";
                    break;
                case "pbBoss1Map":
                    name = "boss1";
                    break;
                case "pbEnemyShelterMap":
                    name = "enemy_shelter";
                    break;
                case "pbStoneFalling":
                    name = "stone_falling";
                    break;
                case "pbStoneFlying":
                    name = "stone_flying";
                    break;
                case "pbFireMap":
                    name = "fire";
                    break;
                case "pbEnemyDiverMap":
                    name = "enemy_diver";
                    break;
                case "pbBoss3Map":
                    name = "boss3";
                    break;
                case "pbEnemyMachine":
                    name = "enemy_machine";
                    break;
                case "pbGrenadeMap":
                    name = "grenade";
                    break;
                case "pbTankMap":
                    name = "tank";
                    break;
                case "pbBoss5Map":
                    name = "boss5";
                    break;
                default:
                    name = "";
                    break;
            }
            int var1, var2;
            try
            {
                var1 = cbbVar1.SelectedIndex;
                var2 = cbbVar2.SelectedIndex;
            }
            catch (Exception ex)
            {
                var1 = 0;
                var2 = 0;
            }
            GameObject obj = new GameObject(id, name, ip, var1, var2);
            Body b = new Body(id , new Point(obj.Point.X, 
                axisY- (obj.Point.Y + obj.Size.Height)), new Size(obj.Size.Width, obj.Size.Height), obj.Type);
            obj.Body = b;
            Rectangle rectObj = new Rectangle(obj.Point, obj.Size);
            foreach (GameObject o in worldmap.ListObjects)
            {
                Rectangle r = new Rectangle(o.Point, o.Size);
                if (((rectObj.X >= r.X && rectObj.X < r.X + r.Size.Width) || (r.X >= rectObj.X && r.X < rectObj.X + rectObj.Size.Width))
                && ((rectObj.Y >= r.Y && rectObj.Y < r.Y + r.Height) || (r.Y >= rectObj.Y && r.Y < rectObj.Y + rectObj.Height)))
                    return;
            }
            this.worldmap.ListObjects.Add(obj);
            this.worldmap.ListBodies.Add(b);
            currentObj = obj;
        }

        //Hàm lấy đối tượng game tại vị trí click
        private GameObject GetObjectsAtPos(int x, int y)
        {
            if (worldmap.ListObjects.Count == 0)
                return null;
            foreach (GameObject o in worldmap.ListObjects)
            {
                Rectangle r = new Rectangle(o.Point, o.Size);
                if (r.Contains(new Point(x, y)))
                {
                    return o;
                }
            }
            return null;
        }

        //Hàm xử lý sự kiện vẽ
        void pbBackground_Paint(object sender, PaintEventArgs e)
        {
            //Vẽ background
            Graphics g = e.Graphics;
            foreach (Stage st in worldmap.ListStages)
            {
                for (int i = 0; i < st.rows; i++)
                {
                    for (int j = 0; j < st.cols; j++)
                    {
                        g.InterpolationMode = InterpolationMode.NearestNeighbor;

                        int index = st.matrixTiles[i, j];
                        g.DrawImage(worldmap.listTiles[index],
                            j * GlobalConstants.TILE_SIZE + st.position.X,
                            i * GlobalConstants.TILE_SIZE + axisY - st.position.Y - st.rows * GlobalConstants.TILE_SIZE,
                            GlobalConstants.TILE_SIZE + 1, GlobalConstants.TILE_SIZE + 1);
                    }
                }
            }
            //Vẽ các đường răng giới hạn
            if (chkShowTiles.Checked)
            {
                //Vẽ các đường răng phân định tiles
                int col = pbBackground.Size.Width / GlobalConstants.STILE_SIZE;
                int row = pbBackground.Height / GlobalConstants.STILE_SIZE;
                Pen p = new Pen(Color.Red, 1.0f);
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                for (int i = 1; i <= row; i++)
                    g.DrawLine(p, new Point(0, i * GlobalConstants.STILE_SIZE),
                        new Point(col * GlobalConstants.STILE_SIZE, i * GlobalConstants.STILE_SIZE));
                for (int i = 0; i < col; i++)
                    g.DrawLine(p, new Point(i * GlobalConstants.STILE_SIZE, 0),
                        new Point(i * GlobalConstants.STILE_SIZE, row * GlobalConstants.STILE_SIZE));

                //Vẽ đường răng kết thúc map
                Pen p2 = new Pen(Color.Red, 2.0f);
                p2.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
                g.DrawLine(p2, new Point(0, axisY), new Point(col * GlobalConstants.TILE_SIZE, axisY));
                g.DrawLine(p2, new Point(axisX, 0), new Point(axisX, col * GlobalConstants.TILE_SIZE));
            }

            //Vẽ các đối tượng game
            foreach (GameObject obj in worldmap.ListObjects)
            {
                Point p = obj.Point;
                p.Y = axisY - p.Y - obj.Size.Height;
                g.InterpolationMode = InterpolationMode.NearestNeighbor;

                Rectangle r = new Rectangle(p, obj.Size);
                g.DrawImage(obj.getImageByType(), r);
            }
            //Vẽ đường biên bao đối tượng game khi được chọn
            if (rect.Width > 0)
            {
                Pen p2 = new Pen(Color.Yellow, 2.0f);
                g.DrawRectangle(p2, rect);
            }
            //Vẽ đường biên bao tất cả các box
            foreach (Body b in worldmap.ListBodies)
            {
                if (b.Size.Width > 0 && b.BodyType < 10)
                {
                    Brush br;
                    switch (b.BodyType)
                    { 
                        case GlobalConstants.GRASS_BODY_ID:
                            br = new SolidBrush(Color.FromArgb(200, 0, 255, 0));
                            break;
                        case GlobalConstants.GRASS1_BODY_ID:
                            br = new SolidBrush(Color.FromArgb(200, 69, 69, 69));
                            break;
                        case GlobalConstants.WATER_BODY_ID:
                            br = new SolidBrush(Color.FromArgb(200, 255, 125, 0));
                            break;
                        case GlobalConstants.SBOX_BODY_ID:
                            br = new SolidBrush(Color.FromArgb(200, 255, 0, 255));
                            break;
                        default:
                            br = new SolidBrush(Color.FromArgb(200, 0, 0, 0));
                            break;
                    }
                    g.FillRectangle(br, new Rectangle(b.Point.X, b.Point.Y, b.Size.Width, b.Size.Height));
                }
            }
            //Vẽ đường biên bao box khi được chọn
            if (rectBox.Width > 0)
            {
                Pen p3 = new Pen(Color.Pink, 2.0f);
                g.DrawRectangle(p3, rectBox);
            }
        }

        //Hàm xử lý checkbox Show Tile
        private void chkShowTiles_CheckedChanged(object sender, EventArgs e)
        {
            if (pbBackground != null) {
                pbBackground.Invalidate();
            }
        }

        //Hàm xử lý sự kiện KeyDown
        private void MapEditor_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ControlKey)
            {
                if (selectedObj.Name != "pbWater" && selectedObj.Name != "pbGrass" && selectedObj.Name != "pbGrass1") 
                {
                    Bitmap curs = new Bitmap(selectedObj.Image);
                    pnlMapViewer.Cursor = CustomCursor.CreateCursor(curs, selectedObj.Image.Width, selectedObj.Image.Height);
                    if (pbBackground != null)
                        pbBackground.Invalidate();
                }
            }
            else if (e.KeyCode == Keys.ShiftKey)
            {
                Bitmap bm = (Bitmap)global::KontoraME.Properties.Resources.eraser;
                pnlMapViewer.Cursor = CustomCursor.CreateCursor(bm, bm.Width, bm.Height);
                if (pbBackground != null)
                    pbBackground.Invalidate();
            }
            else if (e.KeyCode == Keys.Menu)
            {
                if (pbBackground != null)
                    pbBackground.Invalidate();
            }
        }

        //Hàm xử lý sự kiện KeyUp
        private void MapEditor_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ControlKey || e.KeyCode == Keys.ShiftKey || e.KeyCode == Keys.Menu)
            {
                selectedObj = listObj[listObjIndex];
                if (pnlMapViewer.Cursor != Cursors.Arrow)
                    pnlMapViewer.Cursor = Cursors.Arrow;

                if (currentObj != null)
                {
                    currentObj = null;
                }
                if (currentBody != null) {
                    currentBody = null;
                }
                rect = new Rectangle(0, 0, 0, 0);
                if(count == 0) rectBox = new Rectangle(0, 0, 0, 0);
                if (pbBackground != null)
                    pbBackground.Invalidate();
            }
            
        }

        //Hàm xử lý khi thay đổi Stage
        private void cbbStage_SelectedIndexChanged(object sender, EventArgs e)
        {
            int id = cbbStage.SelectedIndex;
            Stage st = worldmap.ListStages[id];
            if (st != null)
            {
                txtStageRow.Text = ((st as Stage).position.X / 32).ToString();
                txtStageCol.Text = ((st as Stage).position.Y / 32).ToString();
                currentStage = st;
            }
        }

        //Hàm xử lý button Save
        private void btnSave_Click(object sender, EventArgs e)
        {
            if (this.worldmap != null)
            {
                SaveFileDialog dlg = new SaveFileDialog();

                dlg.Filter = "map files (*.txt)|*.txt";
                dlg.Title = "Save map";
                dlg.CheckPathExists = true;
                dlg.CheckPathExists = true;
                dlg.ShowDialog();
                if (dlg.FileName != "")
                {
                    Size s;
                    
                    int maxCDimension = 
                        pbBackground.Size.Width > pbBackground.Height ? pbBackground.Width : pbBackground.Height;
                    s = new Size(maxCDimension, maxCDimension);
                    Point p = new Point(0, maxCDimension);
                    rootNodeCollision = new Node(0, p, s, maxCDimension);
                    int a = 0;
                    int maxDimension = pbBackground.Size.Width > pbBackground.Height ? pbBackground.Width : pbBackground.Height;
                    for (int i = 0; i < 10; i++) 
                    {
                        if (Math.Pow(2, i) >= (double)maxDimension / 640)
                        {
                            a = i;
                            break;
                        }
                    }
                    maxDimension = (int)Math.Pow(2, a) * 640;
                    p = new Point(0, maxDimension);
                    s = new Size(maxDimension, maxDimension);
                    Rectangle r = new Rectangle(p, s);
                    rootNode = new Node(0, p, s, maxDimension);
                    foreach (Stage st in worldmap.ListStages)
                    {
                        for (int i = 0; i < st.rows; i++)
                        {
                            for (int j = 0; j < st.cols; j++)
                            {
                                GameObject o = new GameObject(i * st.cols + j, GlobalConstants.TILE_ID,
                                    new Point(j * GlobalConstants.TILE_SIZE, axisY - GlobalConstants.TILE_SIZE - 
                                        i * GlobalConstants.TILE_SIZE), 0, 0);
                                o.Point = new Point(o.Point.X + GlobalConstants.TILE_SIZE / 2,
                                    o.Point.Y + GlobalConstants.TILE_SIZE / 2);
                                this.worldmap.ListObjects.Add(o);
                            }
                        }
                    }
                    List<GameObject> tmplst = new List<GameObject>(worldmap.ListObjects);
//                    worldmap.ListObjects.Clear();
                    GameObject tmpObj;
                    Point point;
                    foreach (GameObject o in tmplst)
                    {
                        if (o.Type == GlobalConstants.TILE_ID)
                        {
                            //o.Id = worldmap.ListObjects.IndexOf(o);
                            //point = new Point(o.Point.X, r.Height - o.Size.Height - o.Point.Y);
                            tmpObj = new GameObject(o.Id, o.Type, o.Point, o.Value1, o.Value2);
                            rootNode.Insert(tmpObj);
                        }
                        else 
                        {
                            //o.Id = worldmap.ListObjects.IndexOf(o) + worldmap.tileNum;
                            point = new Point(o.Point.X + o.Size.Width / 2, o.Point.Y + o.Size.Height / 2);
                            tmpObj = new GameObject(o.Id, o.Type, point, o.Value1, o.Value2);
                            rootNode.Insert(tmpObj);
                        }
                        
                    }
                    foreach (Body b in worldmap.ListBodies) 
                    {
                        b.Point = new Point(b.Point.X, b.Point.Y);
                        rootNodeCollision.InsertBody(b, axisY);
                    }
                    worldmap.saveMap(dlg.FileName, rootNode, rootNodeCollision);
                }
            }
        }

        //Hàm xử lý button Load
        private void btnLoad_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog dg = new OpenFileDialog();
                dg.Filter = "Map Files (txt)|*.txt";
                dg.Multiselect = false;
                if (dg.ShowDialog() == DialogResult.OK)
                {
                    worldmap = new WorldMap();
                    rootNode = new Node(0, new Point(), new Size(), 10240);
                    worldmap.loadMap(dg.FileName, rootNode);
                    countID = worldmap.MaxID;
                    lastScroll = new Point(0, 0);
                    this.cbbStage.DisplayMember = "Name";
                    this.cbbStage.ValueMember = "Value";
                    this.cbbStage.Items.Clear();
                    foreach (Stage st in worldmap.ListStages)
                        this.cbbStage.Items.Add(new { Name = "State " + worldmap.ListStages.IndexOf(st), Value = st });
                    this.cbbStage.SelectedIndex = this.cbbStage.Items.Count - 1;
                    this.DrawWorld();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error! \n" + ex.Message);
            }
        }

        //Hàm xử lý button Clear
        private void btnClear_Click(object sender, EventArgs e)
        {
            if (worldmap != null)
            {
                worldmap.ListObjects.Clear();
                worldmap.ListBodies.Clear();
                pbBackground.Invalidate();
            }
        }

        private void cbbVar1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(currentObj != null)
                currentObj.Value1 = cbbVar1.SelectedIndex;
        }

        private void cbbVar2_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (currentObj != null)
                currentObj.Value2 = cbbVar2.SelectedIndex;
        }
    }
}
